package com.qidi.mvcframework.servlet;

import com.qidi.mvcframework.servlet.annotation.MyController;
import com.qidi.mvcframework.servlet.annotation.MyRequestMapping;
import com.qidi.mvcframework.servlet.context.MyApplicationContext;
import com.qidi.mvcframework.servlet.webmvc.servlet.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 委派：负责任务调度
 */
public class MyDispatcherServlet extends HttpServlet {

    private MyApplicationContext ctx = null;

    private List<MyHandlerMapping> handlerMappings = new ArrayList<>();

    private Map<MyHandlerMapping, MyHandlerAdapter> handlerAdapters = new HashMap<>();

    private List<MyViewResolver> viewResolvers = new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //6 根据 URL 分发给具体的分发
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            try {
                processDispatchResult(req, resp, new MyModelAndView("500"));
            } catch (Exception exception) {
                resp.getWriter().write("500 服务端错误：\n" + Arrays.toString(e.getStackTrace()));
                exception.printStackTrace();
            }
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {

        // 1 通过 url 获取 handler
        MyHandlerMapping handler = getHandler(req);

        if (handler == null) {
            processDispatchResult(req, resp, new MyModelAndView("404"));
            return;
        }

        // 2 根据 handler 获取一个 handlerAdapter
        MyHandlerAdapter adapter = getHandlerAdapter(handler);

        // 3 解析某个方法的形参和返回值之后，统一分装为 ModelAndView
        MyModelAndView mv = adapter.handler(req, resp, handler);

        // 4 把 ModelAndView 变成一个 ViewResolver
        processDispatchResult(req, resp, mv);
    }

    private MyHandlerAdapter getHandlerAdapter(MyHandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()) return null;

        return this.handlerAdapters.get(handler);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, MyModelAndView mv) throws Exception {
        if (mv == null) return;

        if (this.viewResolvers.isEmpty()) return;

        for (MyViewResolver viewResolver : this.viewResolvers) {
            MyView view = viewResolver.resolveViewName(mv.getViewName());
            // 直接向浏览器输出
            view.render(mv.getModel(), req, resp);
            return;
        }
    }

    private MyHandlerMapping getHandler(HttpServletRequest req) {
        if (this.handlerMappings.isEmpty()) return null;


        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();

        String url = uri.replaceAll(contextPath, "").replaceAll("/+", "/");

        for (MyHandlerMapping mapping : handlerMappings) {
            if (mapping.getPattern().matcher(url).matches()) return mapping;
        }

        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 1 加载配置文件 初始化 ApplicationContext
        ctx = new MyApplicationContext(config.getInitParameter("contextConfigLocation"));

        // ====================== MVC ======================
        // 初始化九大组件 （这里只有 3 个）
        initStrategies(ctx);
        System.out.println("My Spring Framework is init.");
    }

    private void initStrategies(MyApplicationContext ctx) {
        // handlerMapping
        initHanlderMappings(ctx);

        // 参数配置器
        initHandlerAdapters(ctx);

        // 视图转换器
        initViewResolvers(ctx);
    }

    private void initViewResolvers(MyApplicationContext ctx) {
        String templateRoot = ctx.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);

        for (File file : templateRootDir.listFiles()) {
            viewResolvers.add(new MyViewResolver(templateRoot));
        }
    }

    private void initHandlerAdapters(MyApplicationContext ctx) {
        for (MyHandlerMapping mapping : handlerMappings) {
            this.handlerAdapters.put(mapping, new MyHandlerAdapter());
        }
    }

    private void initHanlderMappings(MyApplicationContext ctx) {
        if (ctx.getBeanDefinitionCount() == 0) return;

        for (String beanName : ctx.getBeanDefinitionNames()) {
            Object instance = ctx.getBean(beanName);
            Class<?> clazz = instance.getClass();
            if (!clazz.isAnnotationPresent(MyController.class)) continue;

            String baseUrl = "";
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value().trim();
            }

            for (Method method : clazz.getMethods()) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) continue;
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);

                // 处理
                // Spring 中 * 可以代表任意字符 如 add* 就是 正则中的 add.*
                // 多个"/" 统一 替换成 一个 "/"
                String regex = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("\\*", ".*").replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);
                handlerMappings.add(new MyHandlerMapping(pattern, instance, method));
                System.out.println("映射完成：" + regex + "\t" + method);
            }
        }
    }

}
